﻿

namespace Common.Algorithm.Core;

/// <summary>
/// 函数最优实例
/// </summary>
public class SimpleGeneticFunctionService
{
    /// <summary>
    /// 随机数生成器
    /// </summary>
    private static Random randomBuilder = new Random();

    /// <summary>
    /// 交叉数量 一般为总数一般
    /// </summary>
    public int crossoverNum { get; set; } = 50;

    /// <summary>
    /// 编译数量 一般为种群的 1/10
    /// </summary>
    public int mutationNum { get; set; } = 10;

    /// <summary>
    /// 种群总数
    /// </summary>
    public int populationSize { get; set; } = 100;

    /// <summary>
    /// 目标函数  一般就是适应度函数
    /// </summary>
    public Func<int[], double> targetFunc { get; set; }

    /// <summary>
    /// 种群总的基因表
    /// </summary>
    List<int[]> sequences = new List<int[]>();

    /// <summary>
    /// 基因链长度
    /// </summary>
    public int sequenceLen { get; set; } = 32;

    /// <summary>
    /// 最佳基因暂存
    /// </summary>
    public int[] bestSequence { get; set; }
    //==================

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="_targetFunc">目标函数  一般就是适应度函数</param>
    public SimpleGeneticFunctionService(Func<int[], double> _targetFunc)
    {
        targetFunc = _targetFunc;

        bestSequence = new int[sequenceLen];

        sequences.Clear();
        //建立群
        for (int i = 0; i < populationSize; i++)
        {
            List<int> sequence = new List<int>();
            for (int j = 0; j < sequenceLen; j++)
            {
                var value = randomBuilder.NextDouble() >= 0.5 ? 1 : 0;
                sequence.Add(value);
            }
            sequences.Add(sequence.ToArray());
        }
    }

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="_populationSize"></param>
    /// <param name="_sequenceLen"></param>
    /// <param name="_targetFunc">目标函数  一般就是适应度函数  这里函数内采用了二进制转码</param>
    public SimpleGeneticFunctionService(int _populationSize, int _sequenceLen, Func<int[], double> _targetFunc)
    {
        targetFunc = _targetFunc;

        sequenceLen = _sequenceLen;
        populationSize = _populationSize;
        crossoverNum = _populationSize / 2;
        mutationNum = _populationSize / 10;

        bestSequence = new int[sequenceLen];

        sequences.Clear();
        //建立群
        for (int i = 0; i < populationSize; i++)
        {
            List<int> sequence = new List<int>();
            for (int j = 0; j < sequenceLen; j++)
            {
                var value = randomBuilder.NextDouble() >= 0.5 ? 1 : 0;
                sequence.Add(value);
            }
            sequences.Add(sequence.ToArray());
        }
    }

    /// <summary>
    /// 迭代主程
    /// </summary>
    /// <param name="numberOfGeneration"></param>
    public void RunAlgm(int numberOfGeneration)
    {
        List<int[]> population = sequences;
        //for (int i = 0; i < populationSize; i++)
        //{
        //    population.Add(path.OrderBy(p => randomBuilder.Next(path.Count())).ToArray());
        //}

        while (numberOfGeneration > 0)
        {
            numberOfGeneration--;

            List<int[]> childs = new List<int[]>();
            for (int i = 0; i < crossoverNum; i++)
            {
                var father = population[randomBuilder.Next(population.Count)];
                var mother = population[randomBuilder.Next(population.Count)];
                var child = Crossover(father, mother, father.Count() / 2);
                if (child.Length != sequenceLen) continue;//染色体数量变化  定义为其他物种 不计入种群
                childs.Add(child);
            }

            List<int[]> mutates = new List<int[]>();
            Parallel.For(0, 12, (item) =>
            {
                var mutate = population[randomBuilder.Next(population.Count)];
                mutate = Mutation(mutate, randomBuilder.Next(mutate.Count()));
                if (mutate.Length == sequenceLen)  //染色体数量变化  定义为其他物种 不计入种群
                    mutates.Add(mutate);
            });
            //for (int i = 0; i < mutationNum; i++)
            //{
            //    var mutate = population[randomBuilder.Next(population.Count)];
            //    mutate = Mutation(mutate, randomBuilder.Next(mutate.Count()));
            //    if (mutate.Length != sequenceLen) continue;//染色体数量变化  定义为其他物种 不计入种群
            //    mutates.Add(mutate);
            //}

            population = population.Concat(childs).Concat(mutates).ToList();
            sequences = Selection(population).ToList();

            //population = population.GetRange(0, populationSize); 
            //bool hasSame =   population.Any(p => p.GroupBy(v => v).Any(g => g.Count() > 1));
            // p.GroupBy(v => v).Any(g => g.Count() > 1)

        }

        bestSequence = sequences.First();
    }


    /// <summary>
    /// 染色体互换
    /// </summary>
    /// <param name="_pathSt"></param>
    /// <param name="_pathEd"></param>
    /// <param name="_num"></param>
    /// <returns></returns>
    public static int[] Crossover(int[] _pathSt, int[] _pathEd, int _num)
    {
        List<int> pathSt = _pathSt.ToList();
        List<int> pathEd = _pathEd.ToList();

        List<int> childChromosome = new List<int>();

        int num = _num;

        List<int> removeIndexs = new List<int>();
        for (int i = 0; i < num; i++)
        {
            removeIndexs.Add(randomBuilder.Next(pathEd.Count - 1));
        }

        List<int> removeValues = new List<int>();
        removeIndexs.ForEach(i => removeValues.Add(pathEd[i]));
        removeValues.ForEach(v => pathEd.Remove(v));
        var stSegment = pathSt.Where(p => removeValues.Contains(p)).ToList();

        childChromosome = pathEd.Concat(stSegment).ToList();

        return childChromosome.ToArray();
    }


    /// <summary>
    /// 指定编译片段长度
    /// </summary>
    /// <param name="_path"></param>
    /// <param name="_num"></param>
    /// <returns></returns>
    public static int[] Mutation(int[] _path, int _num)
    {
        var sourceChromosome = _path.ToList();
        int num = _num;

        var targetChromosome = _path.ToList();
        //防止完全超变异发生
        if (num > sourceChromosome.Count())
        {
            num = Int32.Parse((num / 2).ToString());
        }

        var minIndex = randomBuilder.Next(sourceChromosome.Count - num - 1);
        var maxIndex = minIndex + num;

        var mutationSt = sourceChromosome.GetRange(0, minIndex);
        var mutationSegment = sourceChromosome.GetRange(minIndex, maxIndex - minIndex);
        var mutationEd = sourceChromosome.GetRange(maxIndex, sourceChromosome.Count() - maxIndex);

        mutationSegment = mutationSegment.OrderBy(s => randomBuilder.Next()).ToList();

        targetChromosome = mutationSt.Concat(mutationSegment).Concat(mutationEd).ToList();

        return targetChromosome.ToArray();

    }


    /// <summary>
    /// 选优方法
    /// </summary>
    /// <param name="groups"></param>
    /// <returns></returns>
    public IList<int[]> Selection(IList<int[]> groups)
    {
        groups = groups.OrderBy(p => targetFunc(p)).ToList();
        groups = groups.ToList().GetRange(0, populationSize);
        return groups;
    }
}
